﻿using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.Runtime.InteropServices;
using System.Xaml;

using alib;
using alib.Enumerable;
using alib.BitArray;
using alib.Debugging;
using alib.Matrix;
using alib.Math;
using alib.Collections.ReadOnly;

using agree.configuration;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class Isometrics : has_grammar_base, ISupportInitializeNotification
	{
		public Isometrics(IRtParent parent)
			: base(parent)
		{
		}

		public new TypeUtils TypeUtils
		{
			get { return base.tu; }
			set { base.TypeUtils = value; }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Tfs TopTfs;

		public FsPath FspEmpty;

		public FsPath RuleArgsPath;
		public FsPath KeyDaughterPath;
		public FsPath OrthPath;
		public FsPath RnamePath;

		public byte[] deleted_daughter_map;
		public int[] DeletedDaughters;

		public Type tt_ne_list;
		public Type tt_list;
		public Type tt_empty;
		public Type tt_dlist;
		public Type tt_dlist_last;
		public Edge.Flag tt_key_daughter;

		/// feature index for certain special features
		[DebuggerDisplay("{ftm==null?f_ix_list_head.ToString():ftm[f_ix_list_head].ToString(),nq}")]
		public int f_ix_list_head;
		[DebuggerDisplay("{ftm==null?f_ix_list_tail.ToString():ftm[f_ix_list_tail].ToString(),nq}")]
		public int f_ix_list_tail;
		[DebuggerDisplay("{ftm==null?f_ix_dlist_list.ToString():ftm[f_ix_dlist_list].ToString(),nq}")]
		public int f_ix_dlist_list;
		[DebuggerDisplay("{ftm==null?f_ix_dlist_last.ToString():ftm[f_ix_dlist_last].ToString(),nq}")]
		public int f_ix_dlist_last;

		public int FIRST { get { return f_ix_list_head; } }
		public int REST { get { return f_ix_list_tail; } }
		public int LIST { get { return f_ix_dlist_list; } }
		public int LAST { get { return f_ix_dlist_last; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// runtime utilities. These mostly pertain to features. For types, see 'tu'
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



		public Type GetMaximalTypeForFeatures(IEnumerable<int> ie_fix)
		{
			return tu.GetGlbOfMany(ie_fix.Select(fix => GetMaximalTypeForFeature(fix)));
		}

		public Type GetMaximalTypeForFeatures(IEnumerable<String> ies_feats)
		{
			return GetMaximalTypeForFeatures(ies_feats.Select(s_feat => ftm[s_feat].i_feat));
		}

		public Type GetMaximalTypeForFeature(int i_feat)
		{
			return i_feat == -1 ? td.ΔTop : ftm[i_feat].introduced_by;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// create a mapping of feature to introducing type, check for multiple introduction, and
		/// incidentally determine the count of distinct features
		/// 
		/// Validate appropriateness conditions and determine 
		/// maximal types for all features. Ensure the validity of the type hierarchy according to certain properties of 
		/// the formalism.
		/// References:
		/// Ann Copestake "The Compleat LKB" 1993, p. 93.
		/// Carroll, Copestake, Malouf, Oepen - LKB checktypes.lsp
		/// Ann Copestake "Implementing Typed Feature Structure Grammars" 2002
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public void EndInit()
		{
			g.im = this;

			this.FspEmpty = new FsPath(this, String.Empty);

			this.TopTfs = ArrayTfs.get_top_tfs(this, g);

			_post_features_init();
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///	<summary>
		/// Resolve some static types
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _post_features_init()
		{
			FeatureInfo fi;

			///////////////////////////////////////////////////////
			/// TYPE SYSTEM CONFIG 
			var tcfg = AgreeConfig.Types;

			if (!ftm.feat_map.TryGetValue(tcfg.ListHead, out fi))
				throw new TdlException("The list head feature '{0}' was not defined in any TDL file.", tcfg.ListHead);
			f_ix_list_head = fi.i_feat;
			Edge.Flag f_ne = fi.introduced_by.EdgeFlag;

			if (!ftm.feat_map.TryGetValue(tcfg.ListTail, out fi))
				throw new TdlException("The list tail feature '{0}' was not defined in any TDL file.", tcfg.ListTail);
			f_ix_list_tail = fi.i_feat;

			if (tu.UnifyTypesFull(f_ne, fi.introduced_by.EdgeFlag) < 0)
				throw new TdlException("The list head and tail features are not compatible.");
			tt_ne_list = bcpo.type_arr[(int)(f_ne & Edge.Flag.MultiIdMask)];

			if (!td.TryGetType(tcfg.TypesList, out tt_list))
				throw new TdlException("The list type '{0}' was not defined in any TDL file.", tcfg.TypesList);

			if (!td.TryGetType(tcfg.TypesEmptyList, out tt_empty))
				throw new TdlException("The empty list type '{0}' was not defined in any TDL file.", tcfg.TypesEmptyList);

			if (!td.TryGetType(tcfg.TypesDiffList, out tt_dlist))
			{
				Console.Error.WriteLine("warning: The difference list type '{0}' was not defined in any TDL file.", tcfg.TypesDiffList);
				tt_dlist = td.ΔTop;
				//throw new TdlException("The difference list type '{0}' was not defined in any TDL file.", tcfg.typs_diff_list);
			}

			if (ftm.feat_map.TryGetValue(tcfg.DlistList, out fi))
				f_ix_dlist_list = fi.i_feat;
			else
			{
				f_ix_dlist_list = -1;
				Console.Error.WriteLine("warning: The difference list 'LIST' feature '{0}' was not defined in any TDL file.", tcfg.DlistList);
				//throw new TdlException("The difference list 'LIST' feature '{0}' was not defined in any TDL file.", tcfg.f_dlist_list);
			}

			if (ftm.feat_map.TryGetValue(tcfg.DlistLast, out fi))
				f_ix_dlist_last = fi.i_feat;
			else
			{
				f_ix_dlist_last = -1;
				Console.Error.WriteLine("warning: The difference list 'LAST' feature '{0}' was not defined in any TDL file.", tcfg.DlistLast);
				//throw new TdlException("The difference list 'LAST' feature '{0}' was not defined in any TDL file.", tcfg.f_dlist_last);
			}

			tt_dlist_last = tt_list;

			///////////////////////////////////////////////////////
			/// GRAMMAR CONFIG 

			var gcfg = AgreeConfig.Grammar;

			String s = gcfg.KeyDaughterValue;
			if (s == null || !tu.TryGetTypeFlag(s, out tt_key_daughter))
			{
				/// LKB sample grammars do not define the LKB default asserted type '+' for KEY-ARG
				Console.Error.WriteLine("warning: The key daughter path type '{0}' was not defined in any TDL file.", gcfg.KeyDaughterValue);
				//tcfg.typs_key_daughter = tcfg.top_type;
				tt_key_daughter = 0;
				//throw new TdlException("The key daughter path type '{0}' was not defined in any TDL file.", config.typs_key_daughter);
			}

			if (gcfg.DeletedDaughters != null)
			{
				int[] rgdd = gcfg.DeletedDaughters.Select(_s => ftm.feat_map[_s].i_feat).ToArray();
				deleted_daughter_map = new byte[ftm.Count];
				foreach (int i_feat in rgdd)
					deleted_daughter_map[i_feat] = 1;

				DeletedDaughters = rgdd;
			}

			compile_path("rule arguments", gcfg.RuleArgsPath, out RuleArgsPath);
			compile_path("key daughter", gcfg.KeyDaughterPath, out KeyDaughterPath);
			compile_path("orthography", gcfg.OrthPath, out OrthPath);
			compile_path("rule moniker (RNAME)", gcfg.RuleMonikerPath, out RnamePath);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// helper
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void compile_path(String s_for, BootstrapFsPath bp, out FsPath fsp)
		{
			if (bp == null || bp.IsEmpty)
				fsp = FsPath.Empty;
			else
			{
				fsp = new FsPath(this, bp);

				if (fsp.IsEmpty)
					Debug.Print("warning: {0} path '{1}' could not be resolved", s_for, bp);
				else if (fsp.Count < bp.Count)
					Debug.Print("warning: {0} path '{1}' resolved to '{2}'", s_for, bp, fsp);
			}
		}
	};
}